﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;


namespace Nirvana
{
    /// <summary>
    /// 场景配置基类脚本，所有要配置的对象，加载预览对象
    /// </summary>
    [ExecuteInEditMode]
    public class SceneObject : MonoBehaviour
    {
#if UNITY_EDITOR
        private Vector3 lastPosition;
        private GameObject preview,previewObj;
        private float playingTime = 0f;
        private double lastTime =-1.0f;

        /// <summary>
        /// Gets the preview object.
        /// </summary>
        protected GameObject PreviewObject { get { return this.previewObj; } }
        /// <summary>
        /// Check whether has preview.
        /// </summary>
        protected virtual bool HasPreview() { return false; }
        /// <summary>
        /// Build the preview object.
        /// </summary>
        protected virtual GameObject BuildPreview() { return null; }
        /// <summary>
        /// Refresh current preview.
        /// </summary>
        protected void RefreshPreview()
        {
            if (this.preview != null)
            {
                var tmpObj = this.preview;
                this.preview = null;
                EditorApplication.delayCall += () => { DestroyImmediate(tmpObj); };
            }
        }

        private void Awake()
        {

            EditorApplication.playModeStateChanged += (state) =>{ 
                if(state  == PlayModeStateChange.EnteredPlayMode)
                {
                    if (this.preview != null)
                    {
                        var tmpObj = this.preview;
                        this.preview = null;
                        DestroyImmediate(tmpObj); 
                    }
                }
            };

        }



        private void OnEnable()
        {
            if (Application.isPlaying) return;

            EditorApplication.update += UpdatePreviewAnimation;
            this.lastTime = EditorApplication.timeSinceStartup;
            if (this.preview != null) this.preview.SetActive(true);

        }

        private void OnDisable()
        {
            if (Application.isPlaying) return;

            EditorApplication.update -= UpdatePreviewAnimation;
            if (this.preview != null) this.preview.SetActive(false);

        }



        private void OnDestroy()
        {
            if (this.preview != null)
            {
                var tmpObj = this.preview;
                this.preview = null;
                DestroyImmediate(tmpObj);
            }
        }


        private void Update()
        {
            this.UpdateSnapGrid();
            this.UpdatePreview();
        }

        private void UpdateSnapGrid()
        {
            float gridsize = 0.5f;
            float gridsizeInverse = 1.0f / gridsize;

            var x = (0.5f * gridsize) + (Mathf.Floor(transform.position.x * gridsizeInverse) / gridsizeInverse);
            var y = transform.position.y;
            var z = (0.5f * gridsize) + (Mathf.Floor(transform.position.z * gridsizeInverse) / gridsizeInverse);
            if(this.lastPosition != this.transform.position)
            {
                var ray = new Ray(this.transform.position + (10000f * Vector3.up), Vector3.down);
                var hits = Physics.RaycastAll(ray, float.PositiveInfinity, 1 << LayerMask.NameToLayer("Walkable"));

                bool find = false;
                float height = float.NegativeInfinity;
                if(hits != null)
                {
                    foreach(var p in hits)
                    {
                        if(height < p.point.y)
                        {
                            height = p.point.y;
                            find = true;
                        }
                    }
                }
                if (find) y = height;
                this.lastPosition = this.transform.position;
            }
            this.transform.position = new Vector3(x, y, z);
        }

        private void UpdatePreview()
        {
            if (EditorApplication.isPlaying ||
                EditorApplication.isPlayingOrWillChangePlaymode ||
                EditorApplication.isCompiling) return;

            if (!this.HasPreview())
            {
                if(this.preview != null)
                {
                    DestroyImmediate(this.preview);
                    this.preview = null;
                    this.previewObj = null;
                }
                return;
            }

            if(this.preview == null)
            {
                this.preview = new GameObject("Preview");
                this.preview.tag = "EditorOnly";
                this.preview.transform.SetPositionAndRotation(this.transform.position, this.transform.rotation);
                this.preview.transform.localScale = this.transform.localScale;

                this.previewObj = this.BuildPreview();
                if(this.previewObj != null)
                {
                    this.previewObj.transform.SetParent(this.preview.transform, false);
                }

                var effectControl = this.previewObj.GetComponent<EffectControl>();
                if (effectControl != null)
                {
                    effectControl.SimulateInit();
                    effectControl.SimulateStart();
                }
                else
                {
                    float minTime = 0f;
                    var ps = this.preview.GetComponentsInChildren<ParticleSystem>();
                    foreach(var p in ps)
                    {
                        if (minTime < p.main.duration) minTime = p.main.duration;
                    }

                    minTime = 0.5f * minTime;
                    foreach (var p in ps) p.Simulate(minTime, false, false);
                }

                this.SetHideFlags(this.preview, HideFlags.HideAndDontSave);

            }
            else
            {
                this.preview.transform.SetPositionAndRotation(this.transform.position, this.transform.rotation);
                this.preview.transform.localScale = this.transform.localScale;
            }

        }

        private void UpdatePreviewAnimation()
        {
            var deltaTime = (float)(EditorApplication.timeSinceStartup - this.lastTime);
            this.lastTime = EditorApplication.timeSinceStartup;
            this.playingTime += deltaTime;
            if (this.previewObj == null) return;

            var effectControl = this.previewObj.GetComponent<EffectControl>();
            if (effectControl != null) effectControl.SimulateDelta(this.playingTime, deltaTime);


        }

        private void SetHideFlags(GameObject obj,HideFlags flags)
        {
            obj.hideFlags = flags;
            for(int i = 0; i < obj.transform.childCount; ++i)
            {
                var child = obj.transform.GetChild(i);
                this.SetHideFlags(child.gameObject, flags);
            }
        }

#endif

    }
}

